﻿/*
 * Copyright (c) 2011
 * Thomas Rozanski, Geoffrey Wright, Zachary Lynn, Jesse Natalie
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the team nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THOMAS ROZANSKI, GEOFFREY WRIGHT,
 * ZACHARY LYNN, OR JESSE NATALIE BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace Kinected
{
    /// <summary>Allows the user to edit the depth buffer.</summary>
    public partial class MockupWindow : Form
    {
        private enum DrawingState : int { Positioning = 0, Sizing = 1, Done = 2 }
        private DrawingState currentDrawingState;

        private Circle currentCircle;
        private List<Circle> circles;

        public Bitmap WindowBitmap;

        private Boolean windowHasChanged;

        public Boolean WindowHasChanged { get { return windowHasChanged; } set { windowHasChanged = value; } }

        public MockupWindow()
        {
            currentDrawingState = DrawingState.Positioning;
            InitializeComponent();

            // make the window bigger to account for borders
            Size = new System.Drawing.Size(Size.Width + Size.Width - ClientRectangle.Width, Size.Height + Size.Height - ClientRectangle.Height);

            circles = new List<Circle>();

            // used by the mock camera
            WindowBitmap = new Bitmap(640, 480, PixelFormat.Format32bppArgb);

            RedrawWindowBitmap();
        }

        /// <summary>Draw the saved circles to a bitmap.</summary>
        private void RedrawWindowBitmap()
        {
            Graphics graphics = Graphics.FromImage(WindowBitmap);

            // blank out the background
            graphics.FillRectangle(Brushes.Gray, graphics.ClipBounds);

            // Draw already placed circles
            foreach (Circle c in circles)
            {
                graphics.FillEllipse(DepthToColor(c.Height), c.Center.X - c.Radius, c.Center.Y - c.Radius, c.Radius * 2, c.Radius * 2);
            }

            WindowHasChanged = true;
        }

        /// <summary>Convert a depth value to a brush set to a RGBA color.</summary>
        /// <param name="depth">0 to 1</param>
        private Brush DepthToColor(float depth)
        {
            return new SolidBrush(Color.FromArgb((int)(depth * 255), (int)(depth * 255), (int)(depth * 255)));
        }

        /// <summary>Window received a click event.</summary>
        private void MockupWindow_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (currentDrawingState == DrawingState.Positioning)
                {
                    currentCircle = new Circle(new Microsoft.Xna.Framework.Vector2(e.X, e.Y), 0f, 0f);
                }
                currentDrawingState++;
                if (currentDrawingState == DrawingState.Done)
                {
                    currentDrawingState = DrawingState.Positioning;
                    circles.Add(currentCircle);
                    RedrawWindowBitmap();
                }
            }
            // removing a circle
            else if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                if (currentDrawingState != DrawingState.Positioning)
                {
                    currentDrawingState = DrawingState.Positioning;
                }
                else
                {
                    // see which we right clicked and remove it
                    for (int i = 0; i < circles.Count; i++)
                    {
                        Microsoft.Xna.Framework.Vector2 temp = new Microsoft.Xna.Framework.Vector2(e.X, e.Y) - circles[i].Center;
                        if (temp.LengthSquared() < circles[i].Radius * circles[i].Radius)
                        {
                            circles.RemoveAt(i);
                            break;
                        }
                    }
                    RedrawWindowBitmap();
                }
                Invalidate(true);
            }
        }

        /// <summary>Window received a paint event.</summary>
        private void MockupWindow_Paint(object sender, PaintEventArgs e)
        {
            // Blank out the background
            e.Graphics.FillRectangle(Brushes.Gray, e.ClipRectangle);

            // Draw already placed circles
            for (int i = 0; i < circles.Count; i++)
            {
                Circle c = circles[i];
                e.Graphics.FillEllipse(DepthToColor(c.Height), c.Center.X - c.Radius, c.Center.Y - c.Radius, c.Radius * 2, c.Radius * 2);
            }

            // Draw circle currently being placed
            if ((int)currentDrawingState > 0)
            {
                e.Graphics.FillEllipse(DepthToColor(currentCircle.Height), currentCircle.Center.X - currentCircle.Radius,
                    currentCircle.Center.Y - currentCircle.Radius, currentCircle.Radius * 2, currentCircle.Radius * 2);
            }
        }

        /// <summary>Window received a close event.</summary>
        private void MockupWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
        }

        /// <summary>Window received a mouse movement event.</summary>
        private void MockupWindow_MouseMove(object sender, MouseEventArgs e)
        {
            // if we are placing a circle
            if ((int)currentDrawingState > 0)
            {
                if (currentDrawingState == DrawingState.Sizing)
                {
                    Microsoft.Xna.Framework.Vector2 vector = new Microsoft.Xna.Framework.Vector2(e.X, e.Y) - currentCircle.Center;
                    float radius = vector.Length();

                    // is this too close to another circle?
                    for (int i = 0; i < circles.Count; i++)
                    {
                        Circle c = circles[i];
                        float allowableRadius = (c.Center - currentCircle.Center).Length() - c.Radius;
                        if (radius > allowableRadius)
                            radius = allowableRadius;
                    }

                    // check the bounds
                    if (radius > currentCircle.Center.X)
                        radius = currentCircle.Center.X;
                    if (radius > currentCircle.Center.Y)
                        radius = currentCircle.Center.Y;
                    if (radius > 640 - currentCircle.Center.X)
                        radius = 640 - currentCircle.Center.X;
                    if (radius > 480 - currentCircle.Center.Y)
                        radius = 480 - currentCircle.Center.Y;

                    currentCircle.Radius = radius;
                }
                Invalidate(true);
            }
        }
    }
}
